#include "Vision.h"
#include "Level/GameObject.h"
#include "../Transform.h"
#include "../Pathfinding/TileData.h"
#include "../Landscape.h"
#include "../MapObstacle.h"
#include <ECS/Math/Vec2.h>
#include <algorithm>

mtBEGIN(VisionDef)
	mtBASE(SensorDef);
	mtCONSTRUCTOR();
	mtVARIABLE(distance);
	mtVARIABLE(angle);
	mtVARIABLE(filterMask);
mtEND

mtBEGIN(Vision)
	mtBASE(Sensor);
	mtCONSTRUCTOR();
	mtMETHOD(initWith);
	mtMETHOD(onStart);
mtEND

bool Vision::initWith(VisionDef* def)
{
	if (!Sensor::initWith(def))
		return false;

	_angle = def->angle;
	_distance = def->distance;
	_filterMask = def->filterMask;
	_cachedTansform = getComponent<Transform>();
	return true;
}

void Vision::onStart()
{
	_map = Landscape::getInstance()->getMap();
}

void Vision::Query()
{
	_objects.clear();

	ECS::Vec2<int> pos(_cachedTansform->getPosition());
	int d = static_cast<int>(_distance);
	int minX = std::max(pos.x - d, _map->getLeftX());
	int maxX = std::min(pos.x + d, _map->getRightX());
	int minY = std::max(pos.y - d, _map->getBottomY());
	int maxY = std::min(pos.y + d, _map->getTopY());

	for (int x = minX; x <= maxX; ++x )
		for (int y = minY; y <= maxY; ++y)
		{
			MapObstacle* obstacle = (MapObstacle*)_map->getUserData(x, y);
			if (obstacle != nullptr)
			{
				GameObject* go = (GameObject*)obstacle->getAssembly();
				_objects.push_back(go);
			}
		}
}

size_t Vision::getObjectsCount() const
{
	return _objects.size();
}

GameObject*const* Vision::getObjects() const
{
	return _objects.data();
}

GameObject* Vision::getNearest( std::function<bool (GameObject*)> filter /*= nullptr*/ ) const
{
	float distance = std::numeric_limits<float>::max();
	GameObject* result = nullptr;
	auto go = (GameObject*)getAssembly();

	for (size_t i = 0; i < _objects.size(); ++i)
	{
		if ((filter == nullptr) || filter(_objects[i]))
		{
			float d = (_cachedTansform->getPosition() - _objects[i]->getComponent<Transform>()->getPosition()).magnitudeSq();
			if (d < distance)
			{
				result = _objects[i];
				distance = d;
			}
		}
	}

	return result;
}



